package action

import (
	"fmt"
	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"github.com/bytedance/sonic"
	"gorm.io/gorm"
	"ssdevice/common/constant"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"strconv"
	"strings"
	"time"
)

type Ota struct {
	Orm *gorm.DB
}

var taskDeviceModel models.SysTaskDevice
var count int64

func (action *Ota) Execute(topic string, jsonData map[string]interface{}) {
	devSn := strings.Split(topic, "/")[3]

	if strings.HasSuffix(topic, "/inform") { //  /inform上报版本 更新 sys_dev_version 表
		if jsonData["data"] == nil {
			return
		}
		data := jsonData["data"].(map[string]interface{})
		if data["main"] == nil {
			return
		}
		mainVersion := data["main"].(string)
		devVersionModel := models.SysDevVersion{}
		devVersionModel.Sn = devSn
		devVersionModel.DevVersion = mainVersion
		devVersionModel.Orm = action.Orm
		if err := devVersionModel.Add(&devVersionModel); err != nil {
			return
		}
		//} else if strings.HasSuffix(topic, "/upgrade") { // /upgrade 平台下发升级 忽略

	} else if strings.HasSuffix(topic, "/progress") { // /progress 终端上报状态  更新 sys_task_device 表进度
		sid := jsonData["sid"]
		if taskDeviceModel.Orm == nil {
			taskDeviceModel = models.SysTaskDevice{}
			taskDeviceModel.Orm = action.Orm
		}
		process := int(jsonData["data"].(map[string]interface{})["step"].(float64))
		desc := jsonData["data"].(map[string]interface{})["desc"]
		sqlSuccess := " update sys_task_device set   progress = ? , status = ? ,update_time = now() where status = 'doing' and  dev_sn = ? and sid = ? "
		sqlFailed := " update sys_task_device set   progress = ? , status = ? ,error_msg = ? ,update_time = now() where status = 'doing' and dev_sn = ? and sid = ?  "
		sqlDoing := " update sys_task_device set   progress = ? ,update_time = now() where status = 'doing' and dev_sn = ? and  sid = ? "
		var result interface{}
		if process == 100 {
			count++
			err := taskDeviceModel.Orm.Exec(sqlSuccess, process, "done", devSn, int64(sid.(float64))).Error
			if err != nil {
				time.Sleep(10)
				err = taskDeviceModel.Orm.Exec(sqlSuccess, process, "done", devSn, int64(sid.(float64))).Error
				logger.Error(err)
			}
			if count%100 == 0 {
				time.Sleep(50)
			}
		} else if process < 0 {
			taskDeviceModel.Orm.Raw(sqlFailed, process, "failed", utils.If(desc == nil, "", desc).(string), devSn, int64(sid.(float64))).Scan(result)
		} else {
			taskDeviceModel.Orm.Raw(sqlDoing, process, devSn, int64(sid.(float64))).Scan(result)
		}
	} else if strings.HasSuffix(topic, "/download") {
		// /download 内置协议下载文件 下载文件转成hex mqtt返回
		// /iot/sn001/$/ota/download/ack
		/**  hex
		{
		  "sid": 1,
		  "code": 200,
		  "data": "A1A2A3..."
		}
		*/
		sid := jsonData["sid"]
		data := jsonData["data"].(map[string]interface{})
		fileId := data["fileid"].(string)
		offset := convertInterfaceToNumber(data["offset"])
		count := convertInterfaceToNumber(data["count"])
		prefix := env.ConfigOperator.GetString("service.device.download-prefix")
		url := prefix + "/api/file/download/" + fileId
		// 执行
		var result map[string]interface{}
		var err error
		if err, result = utils.DownloadFile(url); err != nil {
			ackTopic := fmt.Sprintf("/iot/%s/%s/ota/download/ack", devSn, constant.TopicPlaceholder)
			content := make(map[string]interface{})

			content["sid"] = sid
			content["code"] = 500
			content["data"] = nil
			content["msg"] = err.Error()
			contentBytes, _ := sonic.Marshal(content)
			mqtt.MqttClient.Publish(ackTopic, contentBytes)
			return
		}
		ackTopic := fmt.Sprintf("/iot/%s/%s/ota/download/ack", devSn, constant.TopicPlaceholder)
		content := make(map[string]interface{})

		var fileBytes []byte
		fileBytes, err = getPageBytes(result["bytesContent"].([]byte), count, offset)
		if err == nil {
			hexData, _ := utils.BytesToHex(fileBytes)
			content["data"] = hexData
			content["sid"] = sid
			content["code"] = 200
		} else {
			content["data"] = nil
			content["sid"] = sid
			content["code"] = 500
			content["msg"] = err.Error()
		}

		contentBytes, _ := sonic.Marshal(content)
		mqtt.MqttClient.Publish(ackTopic, contentBytes)
	}
}

func convertInterfaceToNumber(value interface{}) int64 {
	switch v := value.(type) {
	case int64:
		return v
	case float64:
		return int64(v)
	case string:
		num, err := strconv.ParseInt(v, 10, 64)
		if err == nil {
			return num
		}
	}
	return 0
}

func getPageBytes(data []byte, count int64, offset int64) ([]byte, error) {
	// 计算截取的起始位置和结束位置
	start := offset
	end := offset + count
	if end > int64(len(data)) {
		end = int64(len(data))
	}

	// 截取数据
	page := data[start:end]
	return page, nil
}
